package com.luo.chengrui.framework.sadatapermission.core.annotation;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.luo.chengrui.framework.sadatapermission.core.aop.DataPermissionContextHolder;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author
 * @version 1.0.0
 * @description
 * @createTime 2023/09/06
 */

@Aspect
public class DataPermisionAnnotationParser {


    @Pointcut("@annotation(SaDataPermission)")
    public void dataPermissionPointcut() {
    }

    @Before("dataPermissionPointcut()")
    public void parseMemberValue(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取方法上的指定注解
        SaDataPermission annotation = signature.getMethod().getAnnotation(SaDataPermission.class);
        // 获取注解中的参数
        String permissinCode = getAnnotationValue(joinPoint, annotation.permissinCode());
        if (StrUtil.isNotEmpty(annotation.name()) && StrUtil.isNotEmpty(DataPermissionContextHolder.getPermissionCode())) {
            String contextPermissinCode = DataPermissionContextHolder.getPermissionCode();
            String[] nameAndCode = contextPermissinCode.split("@");
            if (Objects.equals(nameAndCode[0], annotation.name())) {
                permissinCode = nameAndCode[1];
            }
        }
        InvocationHandler handler = Proxy.getInvocationHandler(annotation);
        Field field;
        try {
            field = handler.getClass().getDeclaredField("memberValues");
            field.setAccessible(true);
            Map<String, Object> memberValues = (Map<String, Object>) field.get(handler);
            memberValues.put("permissinCode", permissinCode);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }

    }

    /**
     * 获取注解中传递的动态参数的参数值
     *
     * @param joinPoint
     * @param name
     * @return
     */
    public String getAnnotationValue(JoinPoint joinPoint, String name) {
        String paramName = name;
        // 获取方法中所有的参数
        Map<String, Object> params = getParams(joinPoint);
        // 参数是否是动态的:#{paramName}
        if (paramName.matches("^#\\{\\D*\\}")) {
            // 获取参数名
            paramName = paramName.replace("#{", "").replace("}", "");
            // 是否是复杂的参数类型:对象.参数名
            if (paramName.contains(".")) {
                String[] split = paramName.split("\\.");
                // 获取方法中对象的内容
                Object object = getValue(params, split[0]);
                // 转换为JsonObject
                JSONObject jsonObject = new JSONObject(object);
                // 获取值
                Object o = jsonObject.get(split[1]);
                return String.valueOf(o);
            }
            // 简单的动态参数直接返回
            return String.valueOf(getValue(params, paramName));
        }
        // 非动态参数直接返回
        return name;
    }

    /**
     * 根据参数名返回对应的值
     *
     * @param map
     * @param paramName
     * @return
     */
    public Object getValue(Map<String, Object> map, String paramName) {
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            if (entry.getKey().equals(paramName)) {
                return entry.getValue();
            }
        }
        return null;
    }

    /**
     * 获取方法的参数名和值
     *
     * @param joinPoint
     * @return
     */
    public Map<String, Object> getParams(JoinPoint joinPoint) {
        Map<String, Object> params = new HashMap<>(8);
        Object[] args = joinPoint.getArgs();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String[] names = signature.getParameterNames();
        for (int i = 0; i < args.length; i++) {
            params.put(names[i], args[i]);
        }
        return params;
    }

}
